﻿using System;
using System.Collections.Generic;
using System.Linq;

namespace Fast.Extensions.EventBus
{
    public partial class InMemoryEventBusSubscriptionsManager : IEventBusSubscriptionsManager
    {
        private readonly Dictionary<string, List<Type>> _handlers;
        private readonly List<Type> _eventTypes;

        public event EventHandler<string> OnEventRemoved;

        public InMemoryEventBusSubscriptionsManager()
        {
            _handlers = new Dictionary<string, List<Type>>();
            _eventTypes = new List<Type>();
        }

        public bool IsEmpty => !_handlers.Keys.Any();
        public void Clear() => _handlers.Clear();

        public void AddSubscription<T, TH>()
            where T : IntegrationEvent
            where TH : IIntegrationEventHandler<T>
            => BindEventTypeAndHandlr<T, TH>();

        public void AddSubscriptionException<T, TH>()
           where T : IntegrationEvent
           where TH : IIntegrationEventExceptionHandler<T>
            => BindEventTypeAndHandlr<T, TH>();

        private void BindEventTypeAndHandlr<T, TH>()
            where T : IntegrationEvent
        {
            var eventName = GetEventKey<T>();

            DoAddSubscription(typeof(TH), eventName);

            if (!_eventTypes.Contains(typeof(T)))
            {
                _eventTypes.Add(typeof(T));
            }
        }
        public IEnumerable<Type> GetHandlersForEvent<T>() where T : IntegrationEvent
        {
            var key = GetEventKey<T>();
            return GetHandlersForEvent(key);
        }
        public IEnumerable<Type> GetHandlersForEvent(string eventName) => _handlers[eventName];

        public bool HasSubscriptionsForEvent<T>() where T : IntegrationEvent
        {
            var key = GetEventKey<T>();
            return HasSubscriptionsForEvent(key);
        }
        public bool HasSubscriptionsForEvent(string eventName) => _handlers.ContainsKey(eventName);

        public Type GetEventTypeByName(string eventName) => _eventTypes.SingleOrDefault(t => t.Name == eventName);

        public string GetEventKey<T>() where T : IntegrationEvent
            => GetEventKey(typeof(T));

        public string GetEventKey(Type type)
        {
            return type.Name;
        }

        private void DoAddSubscription(Type handlerType, string eventName)
        {
            if (!HasSubscriptionsForEvent(eventName))
            {
                _handlers.Add(eventName, new List<Type>());
            }

            if (_handlers[eventName].Any(t => t == handlerType))
            {
                throw new ArgumentException(
                    $"处理程序类型 {handlerType.Name} 已被 {eventName} 注册", nameof(handlerType));
            }

            _handlers[eventName].Add(handlerType);
        }
    }
}
